/* $Header: det_ui.c,v 2.2 01/10/02 10:26:05 przybyls Exp $ */

/***********************************************************************/
/*                                                                     */
/*                  Copyright Hewlett-Packard Co. 1991                 */
/*                                                                     */
/***********************************************************************/

/***********************************************************************/
/* INCLUDE FILE NAME:  det_ui.c                                        */
/* ==================                                                  */
/*                                                                     */
/* Author:  John DeGood                                                */
/*                                                                     */
/* COMMENT: Detector user interface functions.                         */
/*                                                                     */
/***********************************************************************/

/*****************************/
/*  PROJECT INCLUDE FILES    */
/*****************************/

#include <typedefs.h>
#include <pcb_defs.h>
#include <i_op.h>
#include <iomap.h>
#include <config.h>
#include <error.h>
#include <p_wkfile.h>
#include <wkfile.h>
#include <ramps.h>
#include <zone_config.h>
#include <zone_status.h>
#include <status.h>
#include <sig_types.h>
#include <sig_ui.h>
#include <detector.h>
#include <p_status.h>
#include <det_ui.h>
#include <exception.h>
#include <glp.h>
#include <err_handler.h>
#include <p_ui.h>

#include <sigpath.h>

/*****************************/
/*   LOCAL INCLUDE FILES     */
/*****************************/

/*****************************/
/*   STATIC PROTOTYPES       */
/*****************************/

static void MaskByte (volatile INT8 *hw, BIT8 *image, BIT8 mask, ON_OFF on_off);
static pDETECTOR_STATUS pDetPtr(POSITION p);
#if 0
static INT32 GetDetDAC(POSITION p);
static UI_ERR SetDetDAC(POSITION p, INT32 value);
#endif

/*****************************/
/*   EXTERNAL VARIABLES      */
/*****************************/

extern void DetectorIOTransfer(void);
extern BIT8 OddCombos( EEPROM_DET_TYPE pneu_id , DET_TYPE card_id );

/*****************************/
/*   IMPLEMENT SECTION       */
/*****************************/

/* MaskByte - set or clear a bit in a write-only 8-bit HW register
              and its memory image using a byte mask */
static void MaskByte (volatile INT8 *hw, BIT8 *image, BIT8 mask, ON_OFF on_off)
{
        INTERRUPTS_OFF

        *hw = *image = (on_off == OFF) ? (*image & ~mask) : (*image | mask);

        INTERRUPTS_ON
}

/**********************************************************************/

/* pDetPtr - convert POSITION to pneumatics pDETECTOR_STATUS */
static pDETECTOR_STATUS pDetPtr(POSITION p)
{
        return ((p == FRONT) ? pFRONT_DETECTOR : pBACK_DETECTOR);
}

/**********************************************************************/

/* GetDetectorType - return installed detector type determined at configuration */
DET_TYPE GetDetectorType( POSITION p )
{
        return (p == FRONT) ? DetectorA_Config.type : DetectorB_Config.type;
}

/**********************************************************************/

/* GetDetectorOffset - return current value of signal (before select-and-combine) */
INT32 GetDetectorOffset(POSITION p)
{
        /* scale to match SIGNAL display */
        return ((p == FRONT) ? Deta_Data : Detb_Data) / SIG_DISP_SCALE_FACTOR;
}

/**********************************************************************/

/* GetDetSignalLabel - returns a pointer to the signal units label */
BIT8 *GetDetSignalLabel(POSITION p)
{
   return ((p == FRONT)? &DetectorA_Config.units_label[0]:&DetectorB_Config.units_label[0]);
}

/**********************************************************************/

/* SetDetSignalLabel - expects a pointer to the signal units label */
UI_ERR SetDetSignalLabel(POSITION p, BIT8 *label)
{
   BIT8 *units, *ptr;
   INT16 length;

   units = (p == FRONT)? &DetectorA_Config.units_label[0]:&DetectorB_Config.units_label[0];

   length = 0;
   ptr    = label;

   while( *ptr++ ) length++;

   if ( length > 10 ) return PARAM_TOO_LARGE;

   do
   {
      *units++ = *label++;
   }
   while ( *label != 0 );

   *units = 0;               /* add the null */
   return OK;
}

/* ClearDetSignalLabel - clear the custom detector units label - not used */
void ClearDetSignalLabel(POSITION p)
{
   BIT8  *units;
   INT16 i;

   units = (p == FRONT)? &DetectorA_Config.units_label[0]:&DetectorB_Config.units_label[0];

   for (i=0;i<DET_UNITS_LABEL_SIZE;i++)
   {
      *units++ = 0;
   }
}

/**********************************************************************/

/* GetDetDiagLinearMode - return the diagnostic linear mode on/off state */
ON_OFF GetDetDiagLinearMode(POSITION p)
{
   return (p == FRONT) ? (ON_OFF)DetaDiagLinearMode:(ON_OFF)DetbDiagLinearMode;
}

/**********************************************************************/

/* SetDetDiagLinearMode - set DSP to either linear or normal mode */
UI_ERR SetDetDiagLinearMode(POSITION p, ON_OFF on_off)
{
   INTERRUPTS_OFF

      if (p == FRONT)
      {
         DetaDiagLinearMode = (BIT8)on_off;  /* record the state */

         Dsp_Out.deta_type = (on_off == OFF)
                           ? (U_INT16)DetectorA.type
                           : (U_INT16)LINEAR_DETECTOR;
      }
      else
      {
         DetbDiagLinearMode = (BIT8)on_off;  /* record the state */

         Dsp_Out.detb_type = (on_off == OFF)
                           ? (U_INT16)DetectorB.type
                           : (U_INT16)LINEAR_DETECTOR;
      }

   INTERRUPTS_ON

   SigDetDiagLinearModeChanged();

   return OK;  /* always succeeds */
}

/**********************************************************************/

/* DetectorIsOn - are the detector ON/OFF setpoints ON? */
INT16 DetectorIsOn(POSITION p)
{
        switch (GetDetectorType(p))
        {
        case ECD:
                /* electrometer and status must not be OFF */
                if (GetDetOnOff(p) == OFF || ECDStatus(p) == OFF)
                {
                        return FALSE;
                }
                break;

        case FID:
        case FPD:
                /* electrometer and status must not be OFF */
                if (GetDetOnOff(p) == OFF ||
                    (( FlameStatus(p) == OFF) && (GetElectrometerType(p) == FALSE)))
                {
                        return FALSE;
                }
                break;

        case NPD:
                /* electrometer and status must not be OFF */
                if (GetDetOnOff(p) == OFF || NPDStatus(p) == OFF)
                {
                        return FALSE;
                }
                break;

        case TCD:
                /* status must not be OFF */
                if (TCDStatus(p) == OFF)
                {
                        return FALSE;
                }
                break;

        case AIB:
        default:
                /* electrometer must not be OFF */
                if (GetDetOnOff(p) == OFF)
                {
                        return FALSE;
                }
                break;
        }

        return TRUE;
}

/**********************************************************************/

/* GetDetOnOff - return the bit value of the hardware image */
ON_OFF GetDetOnOff(POSITION p)
{
   if (GetDetectorType(p) == LDID) return GetLDIDElecOnOff(p);
   else
   {
        /* Return the bit value of the hardware image */
        if (p == FRONT)
        {
                return (DiscreteOut1_Image & DETA_BD_ON) ? ON : OFF;
        }
        else
        {
                return (DiscreteOut1_Image & DETB_BD_ON) ? ON : OFF;
        }
   }
}

/**********************************************************************/

/* GetLDIDOnOff - return the bit value of all three hw bits  */
ON_OFF GetLDIDOnOff(POSITION p)
{
   /* Return the bit value of the hardware image */
   if (p == FRONT)
   {
      return ((DiscreteOut1_Image & DETA_BD_ON) && (DiscreteOut2_Image &(DETA_INPUT1_ON | DETA_INPUT2_ON))) ? ON : OFF;
   }
   else
   {
      return ((DiscreteOut1_Image & DETB_BD_ON) && (DiscreteOut2_Image &(DETB_INPUT1_ON | DETB_INPUT2_ON))) ? ON : OFF;
   }
}

/* GetLdidElecOnOff - return the bit value of the 2 electrometer hw bits  */
ON_OFF GetLDIDElecOnOff(POSITION p)
{
   /* Return the bit value of the hardware image */
   if (p == FRONT)
   {
      return ((DiscreteOut1_Image & DETA_BD_ON) && (DiscreteOut2_Image & DETA_INPUT1_ON)) ? ON : OFF;
   }
   else
   {
      return ((DiscreteOut1_Image & DETB_BD_ON) && (DiscreteOut2_Image & DETB_INPUT1_ON)) ? ON : OFF;
   }
}

/* GetLDIDGlowSetpt - return the Glow Setpoint                            */
ON_OFF GetLDIDGlowSetpt(POSITION p)
{
   /* Return the bit value of the hardware image */
   if (p == FRONT)
   {
      return (ON_OFF) Active_Wkfile.det_flags.flag.deta_hiv;
   }
   else
   {
      return (ON_OFF) Active_Wkfile.det_flags.flag.detb_hiv;
   }
}

/**********************************************************************/

/* GetDetInput0 - Return the bit value of the hardware image */
ON_OFF GetDetInput0(POSITION p)
{
        BIT8 image;

        image = (Det_Diag_Mode) ? DiscreteOut2_Diag : DiscreteOut2_Image;

        if (p == FRONT)
        {
                return (image & DETA_INPUT0_ON) ? ON : OFF;
        }
        else
        {
                return (image & DETB_INPUT0_ON) ? ON : OFF;
        }
}

/**********************************************************************/

/* GetDetInput1 - Return the bit value of the hardware image */
ON_OFF GetDetInput1(POSITION p)
{
        BIT8 image;

        image = (Det_Diag_Mode) ? DiscreteOut2_Diag : DiscreteOut2_Image;

        if (p == FRONT)
        {
                return (image & DETA_INPUT1_ON) ? ON : OFF;
        }
        else
        {
                return (image & DETB_INPUT1_ON) ? ON : OFF;
        }
}

/**********************************************************************/

/* GetDetInput2 - Return the bit value of the hardware image */
ON_OFF GetDetInput2(POSITION p)
{
        BIT8 image;

        image = (Det_Diag_Mode) ? DiscreteOut2_Diag : DiscreteOut2_Image;

        if (p == FRONT)
        {
                return (image & DETA_INPUT2_ON) ? ON : OFF;
        }
        else
        {
                return (image & DETB_INPUT2_ON) ? ON : OFF;
        }
}

/**********************************************************************/

/* GetDetInput3 - Return the bit value of the hardware image */
ON_OFF GetDetInput3(POSITION p)
{
        BIT8 image;

        image = (Det_Diag_Mode) ? DiscreteOut2_Diag : DiscreteOut2_Image;

        if (p == FRONT)
        {
                return (image & DETA_INPUT3_ON) ? ON : OFF;
        }
        else
        {
                return (image & DETB_INPUT3_ON) ? ON : OFF;
        }
}

/**********************************************************************/

/* GetDetInput4 - Return the bit value of the hardware image */
ON_OFF GetDetInput4(POSITION p)
{
        BIT8 image;

        image = (Det_Diag_Mode) ? DiscreteOut1_Diag : DiscreteOut1_Image;

        if (p == FRONT)
        {
                return (image & DETA_INPUT4_ON) ? ON : OFF;
        }
        else
        {
                return (image & DETB_INPUT4_ON) ? ON : OFF;
        }
}

/**********************************************************************/


/* GetDetInput5 - Return the bit value of the hardware image */
ON_OFF GetDetInput5(POSITION p)
{
        BIT8 image;

        image = (Det_Diag_Mode) ? DiscreteOut1_Diag : DiscreteOut1_Image;

        if (p == FRONT)
        {
                return (image & DETA_INPUT5_ON) ? ON : OFF;
        }
        else
        {
                return (image & DETB_INPUT5_ON) ? ON : OFF;
        }
}

/**********************************************************************/

/* SetDetOnOff - set detector on/off hardware and image value       */
/* NOTE:  Do NOT use for changing the setpt, only for state control */
void SetDetOnOff(POSITION p, ON_OFF on_off)
{
        /* set both HW register and RAM image with new on/off value */
        MaskByte(&DISCRETE_OUT1, &DiscreteOut1_Image, (p == FRONT) ? DETA_BD_ON : DETB_BD_ON, on_off);

        /* notify signal path of on/off change */
        DetOnOffChanged();
}

/**********************************************************************/

static const SETPT DET_ONOFF[2][2] = {
{ F_DET_ELECT_OFF, F_DET_ELECT_ON },
{ B_DET_ELECT_OFF, B_DET_ELECT_ON }
};

/* SetDetSetptOnOff - set detector on/off setpt, hardware and image value */
UI_ERR SetDetSetptOnOff(POSITION p, ON_OFF on_off)
{
   if (GetDetectorType(p) == LDID) return SetLDIDElecOnOff(p, on_off);
   else
   {
        INTERRUPTS_OFF

        /* update Active_Wkfile value */
        if (p == FRONT)
        {
                Active_Wkfile.detectors_on_off = (on_off == OFF)
                        ? (Active_Wkfile.detectors_on_off & ~DETA_BD_ON)
                        : (Active_Wkfile.detectors_on_off | DETA_BD_ON);
        }
        else
        {
                Active_Wkfile.detectors_on_off = (on_off == OFF)
                        ? (Active_Wkfile.detectors_on_off & ~DETB_BD_ON)
                        : (Active_Wkfile.detectors_on_off | DETB_BD_ON);
        }
        /* update HW and working values */
        SetDetOnOff( p, on_off );

        INTERRUPTS_ON

        LogSetptChange( (SETPT)DET_ONOFF[(int)p][(int)on_off], 0, 0 );

        return OK;      /* always OK */
   }
}

/* Obsolete?? */
UI_ERR SetLDIDSetptOnOff(POSITION p, ON_OFF on_off)
{
   INTERRUPTS_OFF

   /* update Active_Wkfile value */
   if (p == FRONT)
   {
      Active_Wkfile.detectors_on_off = (on_off == OFF)
             ? (Active_Wkfile.detectors_on_off & ~DETA_BD_ON)
             : (Active_Wkfile.detectors_on_off | DETA_BD_ON);

      if (on_off == ON) Active_Wkfile.det_flags.flag.deta_hiv = 1;
      else              Active_Wkfile.det_flags.flag.deta_hiv = 0;

      /* update HW and working values */
      /* set both HW register and RAM image with new on/off value */
      MaskByte(&DISCRETE_OUT1, &DiscreteOut1_Image, DETA_BD_ON, on_off);

      MaskByte(&DISCRETE_OUT2, &DiscreteOut2_Image, DETA_INPUT1_ON, on_off);

      MaskByte(&DISCRETE_OUT2, &DiscreteOut2_Image, DETA_INPUT2_ON, on_off);

      /* notify signal path of on/off change */
      DetOnOffChanged();
   }
   else
   {
      Active_Wkfile.detectors_on_off = (on_off == OFF)
             ? (Active_Wkfile.detectors_on_off & ~DETB_BD_ON)
             : (Active_Wkfile.detectors_on_off | DETB_BD_ON);

      if (on_off == ON) Active_Wkfile.det_flags.flag.detb_hiv = 1;
      else              Active_Wkfile.det_flags.flag.detb_hiv = 0;

      /* update HW and working values */
      /* set both HW register and RAM image with new on/off value */
      MaskByte(&DISCRETE_OUT1, &DiscreteOut1_Image, DETB_BD_ON, on_off);

      MaskByte(&DISCRETE_OUT2, &DiscreteOut2_Image, DETB_INPUT1_ON, on_off);

      MaskByte(&DISCRETE_OUT2, &DiscreteOut2_Image, DETB_INPUT2_ON, on_off);

      /* notify signal path of on/off change */
      DetOnOffChanged();
   }

   INTERRUPTS_ON

   LogSetptChange( (SETPT)DET_ONOFF[(int)p][(int)on_off], 0, 0 );

   return OK;      /* always OK */
}

UI_ERR SetLDIDElecOnOff(POSITION p, ON_OFF on_off)
{
   INTERRUPTS_OFF

   /* update Active_Wkfile value */
   if (p == FRONT)
   {
      Active_Wkfile.detectors_on_off = (on_off == OFF)
             ? (Active_Wkfile.detectors_on_off & ~DETA_BD_ON)
             : (Active_Wkfile.detectors_on_off | DETA_BD_ON);

      /* update HW and working values */
      /* set both HW register and RAM image with new on/off value */
      MaskByte(&DISCRETE_OUT1, &DiscreteOut1_Image, DETA_BD_ON, on_off);

      MaskByte(&DISCRETE_OUT2, &DiscreteOut2_Image, DETA_INPUT1_ON, on_off);

      /* notify signal path of on/off change */
      DetOnOffChanged();
   }
   else
   {
      Active_Wkfile.detectors_on_off = (on_off == OFF)
             ? (Active_Wkfile.detectors_on_off & ~DETB_BD_ON)
             : (Active_Wkfile.detectors_on_off | DETB_BD_ON);

      /* update HW and working values */
      /* set both HW register and RAM image with new on/off value */
      MaskByte(&DISCRETE_OUT1, &DiscreteOut1_Image, DETB_BD_ON, on_off);

      MaskByte(&DISCRETE_OUT2, &DiscreteOut2_Image, DETB_INPUT1_ON, on_off);

      /* notify signal path of on/off change */
      DetOnOffChanged();
   }

   INTERRUPTS_ON

   LogSetptChange( (SETPT)DET_ONOFF[(int)p][(int)on_off], 0, 0 );

   return OK;      /* always OK */
}

UI_ERR SetLDIDGlowSetpt (POSITION p, ON_OFF on_off)
{
   INTERRUPTS_OFF

   /* update Active_Wkfile value */
   if (p == FRONT)
   {
      if (on_off == ON) Active_Wkfile.det_flags.flag.deta_hiv = 1;
      else              Active_Wkfile.det_flags.flag.deta_hiv = 0;

      /* update HW and working values */
      /* set both HW register and RAM image with new on/off value */
      MaskByte(&DISCRETE_OUT2, &DiscreteOut2_Image, DETA_INPUT2_ON, on_off);

      /* notify signal path of on/off change */
      DetOnOffChanged();
   }
   else
   {
      if (on_off == ON) Active_Wkfile.det_flags.flag.detb_hiv = 1;
      else              Active_Wkfile.det_flags.flag.detb_hiv = 0;

      /* update HW and working values */
      /* set both HW register and RAM image with new on/off value */
      MaskByte(&DISCRETE_OUT2, &DiscreteOut2_Image, DETB_INPUT2_ON, on_off);

      /* notify signal path of on/off change */
      DetOnOffChanged();
   }

   INTERRUPTS_ON

   LogSetptChange( (SETPT)DET_ONOFF[(int)p][(int)on_off], 0, 0 );

   return OK;      /* always OK */
}

/**********************************************************************/

/* SetDetInput0 - set detector input 0 hardware and image value */
void SetDetInput0(POSITION p, ON_OFF on_off)
{
        MaskByte(&DISCRETE_OUT2, &DiscreteOut2_Image, (p == FRONT) ? DETA_INPUT0_ON : DETB_INPUT0_ON, on_off);
}

/* SetDetDiagInput0 - set detector input 0 hardware and image value */
void SetDetDiagInput0(POSITION p, ON_OFF on_off)
{
        MaskByte(&DISCRETE_OUT2, &DiscreteOut2_Diag, (p == FRONT) ? DETA_INPUT0_ON : DETB_INPUT0_ON, on_off);
}

/**********************************************************************/

/* SetDetInput1 - set detector input 1 hardware and image value */
void SetDetInput1(POSITION p, ON_OFF on_off)
{
        MaskByte(&DISCRETE_OUT2, &DiscreteOut2_Image, (p == FRONT) ? DETA_INPUT1_ON : DETB_INPUT1_ON, on_off);
}

/* SetDetDiagInput1 - set detector input 1 hardware and image value */
void SetDetDiagInput1(POSITION p, ON_OFF on_off)
{
        MaskByte(&DISCRETE_OUT2, &DiscreteOut2_Diag, (p == FRONT) ? DETA_INPUT1_ON : DETB_INPUT1_ON, on_off);
}

/**********************************************************************/

/* SetDetInput2 - set detector input 2 hardware and image value */
void SetDetInput2(POSITION p, ON_OFF on_off)
{
        MaskByte(&DISCRETE_OUT2, &DiscreteOut2_Image, (p == FRONT) ? DETA_INPUT2_ON : DETB_INPUT2_ON, on_off);
}

/* SetDetDiagInput2 - set detector input 2 hardware and image value */
void SetDetDiagInput2(POSITION p, ON_OFF on_off)
{
        MaskByte(&DISCRETE_OUT2, &DiscreteOut2_Diag, (p == FRONT) ? DETA_INPUT2_ON : DETB_INPUT2_ON, on_off);
}

/**********************************************************************/

/* SetDetInput3 - set detector input 3 hardware and image value */
void SetDetInput3(POSITION p, ON_OFF on_off)
{
        MaskByte(&DISCRETE_OUT2, &DiscreteOut2_Image, (p == FRONT) ? DETA_INPUT3_ON : DETB_INPUT3_ON, on_off);
}

/* SetDetDiagInput3 - set detector input 3 hardware and image value */
void SetDetDiagInput3(POSITION p, ON_OFF on_off)
{
        MaskByte(&DISCRETE_OUT2, &DiscreteOut2_Diag, (p == FRONT) ? DETA_INPUT3_ON : DETB_INPUT3_ON, on_off);
}

/**********************************************************************/

/* SetDetInput4 - set detector input 4 hardware and image value */
void SetDetInput4(POSITION p, ON_OFF on_off)
{
        MaskByte(&DISCRETE_OUT1, &DiscreteOut1_Image, (p == FRONT) ? DETA_INPUT4_ON : DETB_INPUT4_ON, on_off);
}

/**********************************************************************/

/* SetDetInput5 - set detector input 5 hardware and image value */
void SetDetInput5(POSITION p, ON_OFF on_off)
{
        MaskByte(&DISCRETE_OUT1, &DiscreteOut1_Image, (p == FRONT) ? DETA_INPUT5_ON : DETB_INPUT5_ON, on_off);
}

/**********************************************************************/

/* return current value of detector diagnostic VOut1 */
U_INT16 GetDetVOut1(POSITION p)
{
        return ((p == FRONT) ?  Det_Diaga_Rdg : Det_Diagc_Rdg);
}

/**********************************************************************/

/* return current value of detector diagnostic VOut2 */
U_INT16 GetDetVOut2(POSITION p)
{
        return ((p == FRONT) ?  Det_Diagb_Rdg : Det_Diagd_Rdg);
}

/***********************************************************************/

#if 0
/* GetDetDAC - return current value of detector DAC */
static INT32 GetDetDAC(POSITION p)
{
        return DetSerial[(int)p];
}
#endif

/***********************************************************************/

#if 0
/* SetDetDAC - set value of detector DAC */
static UI_ERR SetDetDAC(POSITION p, INT32 value)
{
        DetSerial[(int)p] = (U_INT16)value;
        return OK;
}
#endif

/***********************************************************************/

/* GetFlame - return flame status */
ON_OFF GetFlame(POSITION p)
{
        return FlameStatus(p);
}

/***********************************************************************/

/* GetLDIDGlowStatus - Return LDID plasma status */
INT32  GetLDIDGlowStatus(POSITION p)
{
   INT32 plasma;

   if (p == FRONT) plasma = (INT32) F_LDID_Plasma;
   else            plasma = (INT32) B_LDID_Plasma;

   if (GetLDIDGlowSetpt(p) == OFF) return OFF;   /* Setpoint only for OFF */
   else
   {
      if (plasma == OFF) return TRANSITION;      /* Igniting */
      else               return plasma;          /* ON or SHUTDOWN */
   }
}

/* GetLDIDGlowLevel - Return LDID plasma voltage level */
INT32  GetLDIDGlowLevel(POSITION p)
{
   INT32 level;

   if (p == FRONT) level = (INT32) Det_Diaga_Rdg;
   else            level = (INT32) Det_Diagc_Rdg;

   return (100 * level)/65535;
}

/**********************************************************************/

static const SETPT FLAME_ONOFF[2][2] = {
{ F_DET_FLAME_OFF, F_DET_FLAME_ON },
{ B_DET_FLAME_OFF, B_DET_FLAME_ON }
};

/* SetFlame - keyboard flame on/off that attempts action regardless of current state */
UI_ERR SetFlame(POSITION p, ON_OFF on_off)
{
        /* extinguish or (re)ignite flame regardless of current flame state */
        if ( on_off == OFF )
        {
                /* turn off fuel and air ON/OFF setpoints -- we leave electronics ON */
                /* NOTE: identical manifold positions are used for both FID and FPD */
                /* DANGER:  SetDetGasState(ON) must NEVER be called from a foreground task!!! */
                (void)SetDetGasState (OFF, pDetPtr(p), FID_FUEL_GAS);
                (void)SetDetGasState (OFF, pDetPtr(p), FID_AIR_GAS);

                /* stop state machine */
                FlameExtinguish(p);
        }
        else
        {
                /* return error if EPC fuel or air flow setpoints are zero */
                /* NOTE: identical manifold positions are used for both FID and FPD */
                if ( IsDetectorEpc(p) &&
                     ( (GetDetectorGasFlowSetpt(pDetPtr(p), FID_FUEL_GAS) == 0) ||
                       (GetDetectorGasFlowSetpt(pDetPtr(p), FID_AIR_GAS)  == 0) ))
                {
                        return INVALID_OP;
                }
                else
                {
                        /* turn on fuel, air, and electronics ON/OFF setpoints */
                        /* NOTE: identical manifold positions are used for both FID and FPD */
                        /* DANGER:  SetDetGasState(ON) must NEVER be called from a foreground task!!! */
                        (void)SetDetGasState (ON, pDetPtr(p), FID_FUEL_GAS);
                        (void)SetDetGasState (ON, pDetPtr(p), FID_AIR_GAS);
                        (void)SetDetSetptOnOff(p, ON);

                        /* start state machine */
                        FlameIgnite(p);
                }
        }

        LogSetptChange( (SETPT)FLAME_ONOFF[(int)p][(int)on_off], 0, 0 );

        return OK;
}

/***********************************************************************/

/*
** DCSetFlame - special flame on/off for datacomm so method load does not cause unnecessary flame reignites.
**              This does not check for H2 or air flow == 0 as in SetFlame(), because we want method load to
**              succeed even if the flame is turned on with zero flow.
*/
UI_ERR DCSetFlame(POSITION p, ON_OFF on_off)
{
        /* extinguish flame if setpoint is OFF and status is not OFF */
        if ( (on_off == OFF) && (FlameStatus(p) != OFF))
        {
                FlameExtinguish(p);
        }
        /* (re)start ignition state machine if setpoint is ON and status is not ON */
        else if ( (on_off != OFF) && (FlameStatus(p) != ON) )
        {
                FlameIgnite(p);
        }

        LogSetptChange( (SETPT)FLAME_ONOFF[(int)p][(int)on_off], 0, 0 );

        return OK;
}

/***********************************************************************/

/* GetFIDLitOffset - return det_lit_offset value (units = pA X 10) */
INT32 GetFIDLitOffset(POSITION p)
{
        return Active_Wkfile.det_lit_offset[(int)p];
}

/***********************************************************************/

static const SETPT LIT_OFFSET[2] = { F_DET_LIT_OFFSET, B_DET_LIT_OFFSET };

/* SetFIDLitOffset - set det_lit_offset value (units = pA X 10) */
UI_ERR SetFIDLitOffset(POSITION p, INT32 value)
{
        if (value < FID_LIT_OFFSET_MIN)
        {
                return PARAM_TOO_SMALL;
        }
        else if (value > FID_LIT_OFFSET_MAX)
        {
                return PARAM_TOO_LARGE;
        }
        else
        {
                Active_Wkfile.det_lit_offset[(int)p] = value;
        }

        LogSetptChange( (SETPT)LIT_OFFSET[(int)p], value, 1 );

        return OK;
}

/***********************************************************************/

/* GetTCDFilament - return TCD filament status */
ON_OFF GetTCDFilament(POSITION p)
{
        return TCDStatus(p);
}

/**********************************************************************/

static const SETPT FIL_ONOFF[2][2] = {
{ F_DET_FIL_OFF, F_DET_FIL_ON },
{ B_DET_FIL_OFF, B_DET_FIL_ON }
};

/* SetTCDFilament - turn TCD filament on or off from keyboard */
UI_ERR SetTCDFilament(POSITION p, ON_OFF on_off)
{
        if (on_off == OFF)
        {
                /* turn off ref gas setpoint */
                /* DANGER:  SetDetGasState(ON) must NEVER be called from a foreground task!!! */
                (void)SetDetGasState (OFF, pDetPtr(p), TCD_REF_GAS);

                /* start "off" state machine*/
                TCDOff(p);  /* sets Active_Wkfile on_off value */
        }
        else
        {
                /* return error if EPC ref gas flow setpoint is zero */
                if ( IsDetectorEpc(p) && (GetDetectorGasFlowSetpt(pDetPtr(p), TCD_REF_GAS) == 0))
                {
                        return INVALID_OP;
                }
                else
                {
                        /* turn on ref gas setpoint */
                        /* DANGER:  SetDetGasState(ON) must NEVER be called from a foreground task!!! */
                        (void)SetDetGasState (ON, pDetPtr(p), TCD_REF_GAS);

                        /* start "on" state machine*/
                        TCDOn(p);  /* sets Active_Wkfile on_off value */
                }
        }

        LogSetptChange( (SETPT)FIL_ONOFF[(int)p][(int)on_off], 0, 0 );

        return OK;
}
/**********************************************************************/

/* DCSetTCDFilament - datacomm function to turn TCD filament on or off */
UI_ERR DCSetTCDFilament(POSITION p, ON_OFF on_off)
{
        if (on_off == OFF)
        {
                /* start "off" state machine*/
                TCDOff(p);  /* sets Active_Wkfile on_off value */
        }
        else
        {
                /* start "on" state machine*/
                (void)SetDetGasState (ON, pDetPtr(p), TCD_REF_GAS);
                TCDOn(p);  /* sets Active_Wkfile on_off value */
        }

        LogSetptChange( (SETPT)FIL_ONOFF[(int)p][(int)on_off], 0, 0 );

        return OK;
}

/**********************************************************************/

/* GetTCDNegPolarity - get TCD negative polarity setpoint */
ON_OFF GetTCDNegPolarity(POSITION p)
{
        /* 0 == negative polarity, 1 == normal polarity */

        if (p == FRONT)
        {
                return (Dsp_Out.deta_param == 0)
                        ? ON : OFF;
        }
        else
        {
                return (Dsp_Out.detb_param == 0)
                        ? ON : OFF;
        }
}

/**********************************************************************/

static const SETPT NEG_POL_ONOFF[2][2] = {
{ F_DET_NEG_POL_OFF, F_DET_NEG_POL_ON },
{ B_DET_NEG_POL_OFF, B_DET_NEG_POL_ON }
};

/* SetTCDNegPolarity - set TCD negative polarity setpoint */
UI_ERR SetTCDNegPolarity(POSITION p, ON_OFF on_off)
{
        /* All other detectors pass the "offset" value to the DSP in this
           parameter, but the TCD uses it to pass the polarity setpoint.
           The DSP does not require an "offset" reading for the TCD. */

        /* 0 == negative polarity, 1 == normal polarity */

        INTERRUPTS_OFF

        if (p == FRONT)
        {
                Active_Wkfile.det_flags.flag.deta_polarity =
                        Dsp_Out.deta_param = (on_off == OFF) ? 1 : 0;
        }
        else
        {
                Active_Wkfile.det_flags.flag.detb_polarity =
                        Dsp_Out.detb_param = (on_off == OFF) ? 1 : 0;
        }

        INTERRUPTS_ON

        LogSetptChange( (SETPT)NEG_POL_ONOFF[(int)p][(int)on_off], 0, 0 );

        return OK;  /* always succeeds */
}

/***********************************************************************/

INT32 GetNPDBeadVoltage(POSITION p)
{
        return Active_Wkfile.npd_bead_voltage[(int)p] ;
}

/***********************************************************************/

INT32 GetNPDBeadDACVolts(POSITION p)
{
        return DetSerial[(int)p] ;
}

/**********************************************************************/

static const SETPT BEAD_POWER[2] = { F_DET_BEAD_POWER, B_DET_BEAD_POWER };

UI_ERR SetNPDBeadVoltage(POSITION p, INT32 value)
{
        ON_OFF onoff;

        if (value < NPD_BEAD_POWER_MIN)
        {
                return PARAM_TOO_SMALL;
        }
        else if (value > NPD_BEAD_POWER_MAX)
        {
                return PARAM_TOO_LARGE;
        }
        else
        {
                /* if detector is off or shutdown, turn it on */
                onoff = NPDStatus(p);
                if (onoff == OFF || onoff == SHUTDOWN)
                {
                        (void)SetNPDBeadTurnOn(p, ON);
                }

                /* update serial setpoint value */
        if (value < DetSerial[(int)p] )
        {
           DetSerial[(int)p] = (U_INT16)value;
        };
            Active_Wkfile.npd_bead_voltage[(int)p] = (U_INT16)value; /* eventually DetSerial[p] equilibrates to "value" */
        }

        LogSetptChange( (SETPT)BEAD_POWER[(int)p], value, 0 );

        return OK;
}

/**********************************************************************/

ON_OFF GetNPDBeadTurnOn(POSITION p)
{
        return NPDStatus(p);
}

/**********************************************************************/

static const SETPT BEAD_POWER_ONOFF[2][2] = {
{ F_DET_BEAD_OFF, F_DET_BEAD_ON },
{ B_DET_BEAD_OFF, B_DET_BEAD_ON }
};

UI_ERR SetNPDBeadTurnOn(POSITION p, ON_OFF on_off)
{
        if (on_off == OFF)
        {
                /* turn off fuel and air ON/OFF setpoints -- we leave electronics ON */
                /* DANGER:  SetDetGasState(ON) must NEVER be called from a foreground task!!! */
                (void)SetDetGasState (OFF, pDetPtr(p), NPD_FUEL_GAS);
                (void)SetDetGasState (OFF, pDetPtr(p), NPD_AIR_GAS);

                /* stop state machine */
                NPDExtinguish(p);
        }
        else  /* (on_off == ON | on_off == WAITING | on_off == SHUTDOWN | on_off == TRANSITION) */
        {
                /* return error if EPC fuel or air flow setpoints are zero */
                if ( IsDetectorEpc(p) &&
                     ( (GetDetectorGasFlowSetpt(pDetPtr(p), NPD_FUEL_GAS) == 0) ||
                       (GetDetectorGasFlowSetpt(pDetPtr(p), NPD_AIR_GAS)  == 0) ))
                {
                        return INVALID_OP;
                }

                /* turn on fuel, air, and electronics ON/OFF setpoints */
                /* DANGER:  SetDetGasState(ON) must NEVER be called from a foreground task!!! */
                (void)SetDetGasState (ON, pDetPtr(p), NPD_FUEL_GAS);
                (void)SetDetGasState (ON, pDetPtr(p), NPD_AIR_GAS);
                (void)SetDetSetptOnOff(p, ON);

                /* start state machine */
                NPDIgnite(p, FALSE);
        }

        LogSetptChange( (SETPT)BEAD_POWER_ONOFF[(int)p][(int)on_off], 0, 0 );

        return OK;
}

/**********************************************************************/

/* DCSetNPDBeadTurnOn - datacomm function to turn NPD bead on or off */
UI_ERR DCSetNPDBeadTurnOn(POSITION p, ON_OFF on_off)
{
        if (on_off == OFF)
        {
                /* stop state machine */
                NPDExtinguish(p);
        }
        else  /* (on_off == ON | on_off == WAITING | on_off == SHUTDOWN | on_off == TRANSITION) */
        {
                /* start state machine */
                NPDIgnite(p, FALSE);
        }

        LogSetptChange( (SETPT)BEAD_POWER_ONOFF[(int)p][(int)on_off], 0, 0 );

        return OK;
}

/**********************************************************************/

INT32 GetNPDTargetOffset(POSITION p)
{
        return Active_Wkfile.det_target_offset[(int)p];
}

/**********************************************************************/

static const SETPT TARGET_OFFSET[2] = { F_DET_TARGET_OFFSET, B_DET_TARGET_OFFSET };

UI_ERR SetNPDTargetOffset(POSITION p, INT32 value)
{
        U_INT16 old;
        UI_ERR retval = OK;

        if (value < NPD_TARGET_MIN)
        {
                return PARAM_TOO_SMALL;
        }
        else if (value > NPD_TARGET_MAX)
        {
                return PARAM_TOO_LARGE;
        }
        else
        {
                old = Active_Wkfile.det_target_offset[(int)p];
                Active_Wkfile.det_target_offset[(int)p] = (U_INT16)value;

                LogSetptChange( (SETPT)TARGET_OFFSET[(int)p], value, 0 );

                /*
                ** Initiate an offset seek if (new value != old value), but
                ** don't do it if any dependent setpoint is wrong, because we
                ** don't want to initiate a seek that will fail on a method load.
                **
                ** The keyboard separately calls StartNPDOffsetSeek() to always
                ** initiate an offset seek after every entry of a target offset.
                */
                if ((value != old) &&
                    (NPDStatus(p) != OFF) &&
                    (GetDetOnOff(p) != OFF) &&
                    (GetDetGasState (pDetPtr(p), NPD_FUEL_GAS) != OFF) &&
                    (GetDetGasState (pDetPtr(p), NPD_AIR_GAS) != OFF) &&
                   !(IsDetectorEpc(p) &&
                     ((GetDetectorGasFlowSetpt(pDetPtr(p), NPD_FUEL_GAS) == 0) ||
                      (GetDetectorGasFlowSetpt(pDetPtr(p), NPD_AIR_GAS)  == 0))))
                {
                        retval = StartNPDOffsetSeek(p);
                }

                return retval;
        }
}

/**********************************************************************/

/* initiate NPD offset seek algorithm */
UI_ERR StartNPDOffsetSeek(POSITION p)
{
        /* return error if EPC fuel or air flow setpoints are zero */
        if ( IsDetectorEpc(p) &&
             ( (GetDetectorGasFlowSetpt(pDetPtr(p), NPD_FUEL_GAS) == 0) ||
               (GetDetectorGasFlowSetpt(pDetPtr(p), NPD_AIR_GAS)  == 0) ))
        {
                return INVALID_OP;
        }

        /* turn on fuel, air, and electronics ON/OFF setpoints */
        /* DANGER:  SetDetGasState(ON) must NEVER be called from a foreground task!!! */
        (void)SetDetGasState (ON, pDetPtr(p), NPD_FUEL_GAS);
        (void)SetDetGasState (ON, pDetPtr(p), NPD_AIR_GAS);
        (void)SetDetSetptOnOff(p, ON);

        /* start seek */
        NPDIgnite(p, TRUE);

        /* NPDIgnite() turns on bead setpoint, so log change */
        LogSetptChange( (SETPT)BEAD_POWER_ONOFF[(int)p][(int)ON], 0, 0 );

        return OK;
}

/**********************************************************************/

/* abort NPD offset seek algorithm */
UI_ERR StopNPDOffsetSeek(POSITION p)
{
        /* we only want to do this if we are in seek mode */
        if (NPDStatus(p) == WAITING || NPDStatus(p) == WAITING2 || NPDStatus(p) == TRANSITION)
        {
                /* abort seek */
                NPDIgnite(p, FALSE);

                /* NPDIgnite() turns on bead setpoint, but since it is already on there is no setpoint change to log */
        }

        return OK;
}

/***********************************************************************/

INT32 GetDetEquibTime(POSITION p)
{
        return Active_Wkfile.det_equib_time[(int)p];
}

/***********************************************************************/

static const SETPT DET_EQUIB_TIME[2] = { F_DET_EQUIB_TIME, B_DET_EQUIB_TIME };

UI_ERR SetDetEquibTime(POSITION p, INT32 deciminutes)
{
        if (deciminutes < 0)
        {
                return PARAM_TOO_SMALL;
        }
        else if (deciminutes > MAX_TIME_SETPT)
        {
                return PARAM_TOO_LARGE;
        }
        else
        {
                Active_Wkfile.det_equib_time[(int)p] = deciminutes;

                LogSetptChange( (SETPT)DET_EQUIB_TIME[(int)p], deciminutes, 0 );

                return OK;
        }
}

/***********************************************************************/

/* GetFPDLitOffset - return det_lit_offset value (units = pA X 10) */
INT32 GetFPDLitOffset(POSITION p)
{
        return Active_Wkfile.det_lit_offset[(int)p];
}

/***********************************************************************/

/* SetFPDLitOffset - set det_lit_offset value (units = pA X 10) */
UI_ERR SetFPDLitOffset(POSITION p, INT32 value)
{
        if (value < FPD_LIT_OFFSET_MIN)
        {
                return PARAM_TOO_SMALL;
        }
        else if (value > FPD_LIT_OFFSET_MAX)
        {
                return PARAM_TOO_LARGE;
        }
        else
        {
                Active_Wkfile.det_lit_offset[(int)p] = value;
        }

        LogSetptChange( (SETPT)LIT_OFFSET[(int)p], value, 1 );

        return OK;
}

/**********************************************************************/

/* ECDDACToRefCurrent - returns DAC value as display units (nanoamps * 100) */

INT32 ECDDACToRefCurrent(INT32 value)
/*
 *
 * The 8-bit DAC outputs 10V FS to a 2 gigaohm resistor offset by -0.798 volts.
 * Therefore:
 *
 *      reference_current = DAC_setpt * 10.798 * 100
 *                          ------------------------
 *                                   2 * 255
 */
{
        INT32 current;

        /* "half-round" because of division truncation */
        current = ((value * 10798) + (255 * 10)) / (2 * 255 * 10);

        if (current < ECD_REF_CURRENT_MIN)
        {
                return ECD_REF_CURRENT_MIN;
        }
        else if (current > ECD_REF_CURRENT_MAX)
        {
                return ECD_REF_CURRENT_MAX;
        }
        else
        {
                return current;
        }
}

/**********************************************************************/

/* ECDRefCurrentToDAC - returns display units (nanoamps * 100) as DAC value */

INT32 ECDRefCurrentToDAC(INT32 value)
/*
 *
 * The 8-bit DAC outputs 10V FS to a 2 gigaohm resistor offset by -0.798 volts.
 * Therefore:
 *
 *      DAC_setpt = reference_current * 2 * 255
 *                  ---------------------------
 *                         10.798 * 100
 */
{
        INT32 dac;

        /* "half-round" because of division truncation */
        dac = ((value * (2 * 255 * 10)) + (10798 / 2)) / 10798;

        if (dac < ECD_DAC_MIN)
        {
                return ECD_DAC_MIN;
        }
        else if (dac > ECD_DAC_MAX)
        {
                return ECD_DAC_MAX;
        }
        else
        {
                return dac;
        }
}

/**********************************************************************/

/* GetECDDAC - Returns ECD cell reference current in DAC units */

INT32 GetECDDAC(POSITION p)
{
        return DetSerial[(int)p];
}

/**********************************************************************/

static const SETPT REF_CURRENT[2] = { F_DET_REF_CURRENT, B_DET_REF_CURRENT };

/* SetECDDAC - Sets ECD cell reference current in DAC units */

UI_ERR SetECDDAC(POSITION p, INT32 value)
{
        if (value < ECD_DAC_MIN)
        {
                return PARAM_TOO_SMALL;
        }
        else if (value > ECD_DAC_MAX)
        {
                return PARAM_TOO_LARGE;
        }
        else
        {
                INTERRUPTS_OFF
                DetSerial[(int)p] = value;
                DetUserSerial[(int)p] = ECDDACToRefCurrent(value);
                INTERRUPTS_ON

                LogSetptChange( (SETPT)REF_CURRENT[(int)p], DetUserSerial[(int)p], 0 );

                return OK;
        }
}

/**********************************************************************/

/* GetECDRefCurrent - Returns ECD cell reference current in display units (nanoamps * 100) */

INT32 GetECDRefCurrent(POSITION p)
{
        return DetUserSerial[(int)p];
}

/**********************************************************************/

/* Sets ECD cell reference current in display units (nanoamps * 100) */

UI_ERR SetECDRefCurrent(POSITION p, INT32 value)
{
        if (value < ECD_REF_CURRENT_MIN)
        {
                return PARAM_TOO_SMALL;
        }
        else if (value > ECD_REF_CURRENT_MAX)
        {
                return PARAM_TOO_LARGE;
        }
        else
        {
                INTERRUPTS_OFF
                DetUserSerial[(int)p] = value;
                DetSerial[(int)p] = ECDRefCurrentToDAC(value);
                INTERRUPTS_ON

                LogSetptChange( (SETPT)REF_CURRENT[(int)p], value, 0 );

                return OK;
        }
}

/***********************************************************************/

INT32 GetECDTargetOffset(POSITION p)
{
        return Active_Wkfile.det_target_offset[(int)p];
}

/**********************************************************************/

UI_ERR SetECDTargetOffset(POSITION p, INT32 value)
{
        U_INT16 old;

        if (value < ECD_TARGET_MIN)
        {
                return PARAM_TOO_SMALL;
        }
        else if (value > ECD_TARGET_MAX)
        {
                return PARAM_TOO_LARGE;
        }
        else
        {
                old = Active_Wkfile.det_target_offset[(int)p];
                Active_Wkfile.det_target_offset[(int)p] = (U_INT16)value;

                /*
                ** Initiate an offset seek if (new value != old value), but
                ** don't do it if any dependent setpoint is wrong, because we
                ** don't want to change the dependent setpoints on a method load!
                **
                ** The keyboard separately calls StartECDOffsetSeek() to always
                ** initiate an offset seek after every entry of a target offset.
                */
                if ((value != old) &&
                    (GetDetOnOff(p) != OFF))
                {
                        (void)StartECDOffsetSeek(p);
                }

                LogSetptChange( (SETPT)TARGET_OFFSET[(int)p], value, 0 );

                return OK;
        }
}

/**********************************************************************/

UI_ERR StartECDOffsetSeek(POSITION p)
{
        /* turn electronics on */
        (void)SetDetSetptOnOff(p, ON);

        /* perform seek */
        ECDStartSeek(p);

        /* not a setpt, so we don't log this */
        return OK;  /* can't fail */
}

/**********************************************************************/

UI_ERR StopECDOffsetSeek(POSITION p)
{
        /* if not OFF then go to the normal ON state */
        if (GetECDStatus(p) != OFF)
        {
                ECDStopSeek(p);
                /* not a setpt, so we don't log this */
        }
        return OK;  /* can't fail */
}

/**********************************************************************/

ON_OFF GetECDStatus(POSITION p)
{
        return ECDStatus(p);
}

/**********************************************************************/
/****** DEBUG *** DEBUG *** DEBUG *** DEBUG *** DEBUG *** DEBUG *******/
/**********************************************************************/

/***********************************************************************/
/*
 * WARNING - The functions which appear below this point must NOT
 * be included in any general release!  They are for use ONLY
 * to "tune" the skew between TCD valve switching and data
 * demodulation.  This code is only valid when used with a
 * special version of DSP software!
 *
 * The normal setpoints that affect these DSP parameters are not
 * disabled, so whoever uses these need to be aware that certain
 * normal setpoints will modify these values!
 *
 * The debug DSP code assumes a single TCD in the front position,
 * so these functions do not pass "position" as a parameter.
 */
/***********************************************************************/

#ifdef DEBUG_TCD_SKEW

INT32 GetTCDStart1(void)
{
        return Dsp_Out.deta_type;
}

INT32 GetTCDStop1(void)
{
        return Dsp_Out.detb_type;
}

INT32 GetTCDStart2(void)
{
        return Dsp_Out.sig1_def;
}

INT32 GetTCDStop2(void)
{
        return Dsp_Out.sig2_def;
}

UI_ERR SetTCDStart1(INT32 value)
{
        Dsp_Out.deta_type = (U_INT16)value;
        return OK;
}

UI_ERR SetTCDStop1(INT32 value)
{
        Dsp_Out.detb_type = (U_INT16)value;
        return OK;
}

UI_ERR SetTCDStart2(INT32 value)
{
        Dsp_Out.sig1_def = (U_INT16)value;
        return OK;
}

UI_ERR SetTCDStop2(INT32 value)
{
        Dsp_Out.sig2_def = (U_INT16)value;
        return OK;
}

#endif /* DEBUG_TCD_SKEW */

/**********************************************************************/
/****** DEBUG *** DEBUG *** DEBUG *** DEBUG *** DEBUG *** DEBUG *******/
/**********************************************************************/

BIT8 GetElectrometerType( POSITION p )
{
   return (p == FRONT) ?                                                  \
                DetectorA_Config.electrometer : DetectorB_Config.electrometer;
}

/* This function returns TRUE if we have an electrometer with no pneumatics */
/* and false otherwise.                                                     */

BIT8 ManualElectrometer( POSITION p )
{
   if ( GetElectrometerType(p) && !GetCPFDetPneuPresence(p) )
   {
      return TRUE;
   }

   return FALSE;
}

UI_ERR SetElectrometerType( POSITION p, BIT8 electrometer )
{
   DET_CONFIG *pconfig;

   pconfig = (p == FRONT) ? &DetectorA_Config : &DetectorB_Config;

   /* Check for proper range
    *    electrometer = 0 - not electrometer
    *    electrometer = 1 - electrometer
    */
   if (electrometer > 1)
   {
      return INVALID_PARAM;
   }
   if ( EpcDetector( (p == FRONT) ? pFRONT_DETECTOR : pBACK_DETECTOR ) )
   {
      return NOT_ALLOWED;
   }

   if ( pconfig->type == FID || pconfig->type == FPD )
   {
      if ( electrometer )
      {
         /* Set flame off */
         (void) SetFlame( p, OFF );
      }

      /* Only init the subtask if the mode has changed */
      if ( electrometer != pconfig->electrometer )
      {
         pconfig->electrometer = electrometer;
         InitDetectorSubtask( (INT32)p );
         if (electrometer)
         {
            ClearException((p==FRONT)?FD_PNEU_ELEC_MISMATCH:BD_PNEU_ELEC_MISMATCH);
         }
         else
         {
            CheckForDetPneuElecMismatches();
         }
      }

/* REVISIT: add Setpt Logging */
      return OK;
   }

   return NOT_ALLOWED;
}

/* SetElectrometerTitle - expects a pointer to the title */
/*      used by datacomm to set title */
UI_ERR SetElectrometerTitle(POSITION p, BIT8 *title)
{
   BIT8 *el_name,*name, *ptr;
   INT16 length;

   el_name = (p==FRONT)? &DetectorA_Config.title[0]:&DetectorB_Config.title[0];
   name = (p==FRONT)? &FDName[0]:&BDName[0];

   length = 0;
   ptr    = title;

   while( *ptr++ ) length++;

   if ( length > DET_NAME_SIZE ) return PARAM_TOO_LARGE;

   do
   {
      *name++ = *title;
      *el_name++ = *title++;
   }
   while ( *title != 0 );

   *el_name = 0;               /* add the null */
   *name = 0;                  /* add the null */
   return OK;
}

/* InstallElectrometerTitle - copies electrometer title from RAM to active */
void InstallElectrometerTitle(POSITION p)
{
   INT16 index;

   if(p == FRONT)
   {
      for(index = 0; index < DET_NAME_SIZE; index++)
      {
         FDName[index] = DetectorA_Config.title[index];
      }
   }
   else
   {
      for(index = 0; index < DET_NAME_SIZE; index++)
      {
         BDName[index] = DetectorB_Config.title[index];
      }
   }
}

/* ClearElectrometerTitle - Clears electrometer title in RAM  */
void ClearElectrometerTitle(POSITION p)
{
   INT16 index;

   if(p == FRONT)
   {
      for(index = 0; index < DET_NAME_SIZE; index++)
      {
         DetectorA_Config.title[index] = 0;
      }
   }
   else
   {
      for(index = 0; index < DET_NAME_SIZE; index++)
      {
         DetectorB_Config.title[index] = 0;
      }
   }
}


static BIT8 SkipDetPneuElecMismatchCheck( POSITION position )
{
   DET_TYPE type;
   BIT16    pneu_type;

   type = GetDetectorType( position );

   pneu_type = (BIT16)GetDetType((position==FRONT) ?                         \
                      pFRONT_DETECTOR:pBACK_DETECTOR);
   pneu_type &= 0x0f;

   /* REVISIT: skip uECD check */
   if (((DET_TYPE)pneu_type == ECD) && ((DET_TYPE)type == uECD)) return TRUE;

   /* Skip the consitency check for the analog input board, */
   /* or the electrometer board.                            */
   if (( type == AIB ) || GetElectrometerType( position ))
   {
      return TRUE;
   }

   return FALSE;
}

void CheckForDetPneuElecMismatches( void )
{
   DET_TYPE         f_det_type, b_det_type;
   EEPROM_DET_TYPE  f_pneu_det, b_pneu_det;

   f_det_type  = DetectorA_Config.type;
   b_det_type  = DetectorB_Config.type;
   f_pneu_det  = Inst_Config.Pneu.Frnt_Detector.Type;
   b_pneu_det  = Inst_Config.Pneu.Back_Detector.Type;

   /* See if we can skip checking for pneu and det types agreeing */
   if ( !SkipDetPneuElecMismatchCheck(FRONT) )
   {
      /* See if the detector board config has the proper epc electronics */
      if ( f_det_type != NO_DET )
      {
         if (
             ( !OddCombos( f_pneu_det, f_det_type ) ) &&
             ((int)f_det_type != ((int)f_pneu_det&0x0f))   /* mask off whether or not it is a manual detector */
            )
         {
            SetException( FD_PNEU_ELEC_MISMATCH );
         }
      }
   }

   /* See if we can skip checking for pneu and det types agreeing */
   if ( !SkipDetPneuElecMismatchCheck(BACK) )
   {
      if ( b_det_type != NO_DET )
      {
         if (
             ( !OddCombos( b_pneu_det, b_det_type ) ) &&
             ((int)b_det_type != ((int)b_pneu_det&0x0f))   /* mask off whether or not it is a manual detector */
            )
         {
            SetException( BD_PNEU_ELEC_MISMATCH );
         }
      }
   }
}
